
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package bittorentlike;
import Util.Utils;
import define.Define;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 *
 * @author Xuan Cau
 */
public class Receive extends Thread
{
    private MainControl main;
    private String ip;
    public Receive(MainControl main)
    {
        this.main = main;
        try
        {
            ip = String.valueOf(InetAddress.getLocalHost().getHostAddress());
            Utils.ReadFileConfig();
        }catch(Exception ex)
        {
            
        }
    }
    public synchronized void run()
    {
        //int port = Define.PORT;
        
        for(int i=0; i< Utils.arr_peer.length; i++)
        {
            
            if(ip.equals(Utils.arr_peer[i].getIp()))
            {
                Utils.port = Utils.arr_peer[i].getPort();
                
                break;
            }
        }
        try
        {
            Utils.socket = new DatagramSocket(Utils.port);
            while(true)
            {
                //Nhan thong diep tu Client
                byte []rcvbuf = new byte[Define.MAX_LEN];
                //Tao DatagramPacket de nhan data
                DatagramPacket rcvpkt = new DatagramPacket(rcvbuf,rcvbuf.length);
                //Goi ham nhan DatagramPacket
                Utils.socket.receive(rcvpkt);
                //InetAddress address;// = new InetAddress();
                //if(address.getAddress()
                //if(rcvpkt.getAddress().equals(InetAddress.getLocalHost()))
                   // return;
                
                ByteBuffer buf = ByteBuffer.allocate(Define.SIZE_OF_INT32+1);
                buf.put(rcvbuf,0,Define.SIZE_OF_INT32+1);
                buf.position(0);
                int len = buf.getInt();
                if(len <= 0)
                {
                    System.out.println("msglen not enought");
                }
                else
                {
                    buf = ByteBuffer.allocate(len);
                    buf.put(rcvbuf,Define.SIZE_OF_INT32,len);
                    buf.position(0);
                    Handle(buf, rcvpkt.getAddress(), rcvpkt.getPort());
                    //System.out.println("ip = " + rcvpkt.getAddress() + "port = " + rcvpkt.getPort()); 
                }
            }
        }
        catch(Exception ex)
        {
            
        }
    }
    public synchronized void Handle(ByteBuffer buf, InetAddress ip, int port)
    {
        int command = (int)buf.get();
       // System.out.println("command ========= " + command);
        switch(command)
        {
            case Define.HELLO:
                handleHello(buf, ip, port);
                break;
            case Define.RESPOND_HELLO:
                handleRespondHello(buf, ip, port);
                break;
            case Define.UPLOAD:
                HandleUpload(buf, ip, port);
                break;
            case Define.DOWNLOAD:
                HandleDownload(buf, ip, port);
                break;
            case Define.RESPOND_DOWNLOAD:
                HandleRespondDownload(buf, ip, port);
                break;
            case Define.SEARCH:
                HandleSearch(buf, ip, port);
                break;
            case Define.RESPOND_SEARCH:
                HandleRespondSearch(buf, ip, port);
                break;
            case Define.CONFIG:
                HandleConfig(buf, ip, port);
                break;
            case Define.RESPOND_CONFIG:
                HandleRespondConfig(buf, ip, port);
                break;
            case Define.DOWNLOAD_TORRENT:
                HandleDownloadTorrent(buf, ip, port);
                break;
            case Define.RESPOND_DOWNLOAD_TORRENT:
                HandleRespondDownloadTorrent(buf, ip, port);
                break;
        }
        
    }
    public void handleHello(ByteBuffer respone, InetAddress ip, int port)
    {
        int len = 0;
        Send send = new Send();
       
        String host_name = Util.Utils.GetHostName();
        byte[] byte_host_name = host_name.getBytes();
        len += byte_host_name.length;
        
       /* byte[] host_address = Util.Utils.GetHostAddrees();
        len += host_address.length;*/
        
        ByteBuffer buf = ByteBuffer.allocate(len + 2);
        buf.putShort((short)byte_host_name.length);
        buf.put(byte_host_name);
        
       /* buf.putShort((short)host_address.length);
        buf.put(host_address);*/
        
        send.SendRespondHello(buf.array(), ip, port);
    }
    public void handleRespondHello(ByteBuffer respone, InetAddress ip, int port)
    {
        int len = (int)respone.getShort();
        byte[] data_recevie = new byte[len];
        respone.get(data_recevie);
        
       /* int len2 = (int)respone.getShort();
        byte[] data_recevie2 = new byte[len2];
        respone.get(data_recevie2);
        System.out.println("" + new String(data_recevie2));*/
        this.main.AddHostName(new String(data_recevie),null);
    }
    public void HandleDownload(ByteBuffer respone, InetAddress ip, int port)
    {
        int len = (int)respone.getShort();
        byte[] data_recevie = new byte[len];
        respone.get(data_recevie);
        ByteBuffer buf = ByteBuffer.allocate(2);
        // tim chunk tuong ung
        Path directory = Paths.get("chunk/");
        try (DirectoryStream<Path> directoryStream = Files
        .newDirectoryStream(directory)) 
        {
            for(Path file : directoryStream) 
            {
                File file_chunk = new File("chunk/" + file.getFileName().toString());
                FileInputStream is_chunk = new FileInputStream(file_chunk);
                DataInputStream in = new DataInputStream(is_chunk);
                byte[] data = new byte[Define.MAX_CHUNK];
                in.read(data);
                if(java.util.Arrays.equals(Util.Utils.SHA1(data),data_recevie))
                {
                    buf = ByteBuffer.allocate(data.length + 2);
                    buf.putShort((short)data.length);
                    buf.put(data);
                    break;
                }
            }
        }
        catch (IOException | DirectoryIteratorException ex) 
        {
            ex.printStackTrace();
        }
        Send send = new Send();
        send.SendRespondDownload(buf.array(), ip, port);
    }
    public void HandleDownloadTorrent(ByteBuffer respone, InetAddress ip, int port)
    {
        // get host name
        int len_host = respone.getShort();
        byte[] host_name = new byte[len_host];
        respone.get(host_name);
        
        // get file name
        int len_file = respone.getShort();
        byte[] file_name = new byte[len_file];
        respone.get(file_name);
        ByteBuffer buf = ByteBuffer.allocate(Define.MAX_LEN);
        int len = 0;
        int num_chunk = 0;
        boolean found = false;
        try
        {
            System.out.println("file can tim :" + new String(file_name));
            Path directory = Paths.get("torrent/");
            try (DirectoryStream<Path> directoryStream = Files
            .newDirectoryStream(directory))
            {
                for(Path file : directoryStream) 
                {
                    String file_name_torrent = file.getFileName().toString();
                    file_name_torrent = Util.Utils.Remove(file_name_torrent, ".torrent");
                    if(java.util.Arrays.equals(file_name,file_name_torrent.getBytes())
                    && java.util.Arrays.equals(host_name,Util.Utils.GetHostName().getBytes()))
                    {
                        found = true;
                        // doc file tim thay.
                        File f = new File("torrent/" + file.getFileName().toString());
                        String f2 = f.getAbsolutePath();
                        File file_chunk = new File(f2);
                        FileInputStream is_chunk = new FileInputStream(file_chunk);
                        DataInputStream in = new DataInputStream(is_chunk);
                        int len_chunk = in.readInt();
                        while(len_chunk !=-1)
                        {
                            len += 2;
                            buf.putShort((short)len_chunk);
                            
                            byte[] data_chunk = new byte[len_chunk];
                            in.read(data_chunk);
                            
                            buf.put(data_chunk);
                            len += len_chunk;
							
                            byte[] endline = new byte[1];
                            in.read(endline);
                            
                            num_chunk += 1;
                            len_chunk = in.readInt();
                        }
                        break;
                    }
                }
            }
        }
        catch(Exception ex)
        {
            
        }
        byte[] data_send = new byte[len + 2];
        
        ByteBuffer buf_num_chunk = ByteBuffer.allocate(2);
        buf_num_chunk.putShort((short)num_chunk);
        buf_num_chunk.position(0);
        buf_num_chunk.get(data_send, 0, 2);
        
        buf.position(0);
        buf.get(data_send,2,len);
        
        if(found)
        {
            Send send = new Send();
            send.SendRespondDownloadTorrent(data_send, ip, port);
        }
    }
    public void HandleRespondDownloadTorrent(ByteBuffer respone, InetAddress ip, int port)
    {
        // nhan file torrent.
        // chuyen file torrent qua main control.
        int num_chunk = respone.getShort();
        byte[][] torrent = new byte[num_chunk][];
        for(int i = 0;i<num_chunk;i++)
        {
            int len = respone.getShort();
            torrent[i] = new byte[len];
            respone.get(torrent[i]);
        }
        main.BeginDownload(torrent);
        /*
        main.InitListChunk(num_chunk);
        for(int i = 0;i<num_chunk;i++)
        {
            int len = respone.getShort();
            byte[] chunk = new byte[len];
            respone.get(chunk);
            // gui qua main control de tien hanh download
            main.DownChunk(chunk,i);
        }*/
    }
    public void HandleRespondDownload(ByteBuffer respone, InetAddress ip, int port)
    {
        System.out.println("HandleRespondDownload");
        int len = (int)respone.getShort();
        byte[] data_recevie = new byte[len];
        respone.get(data_recevie);
        main.NewChunk(data_recevie);
    }
    public void HandleUpload(ByteBuffer respone, InetAddress ip, int port) 
    {
        try
        {
            System.out.println("HandUpload");
            int len_name = (int)respone.getShort();           
            byte[]name_file = new byte[len_name];
            respone.get(name_file);        
            String namefile = new String(name_file, "UTF-8");    
            int len = (int)respone.getShort();
            byte[] data_recevie = new byte[len];        
            respone.get(data_recevie);          
            File file_upload = new File("upload\\" + namefile);           
            FileOutputStream fop;
            fop = new FileOutputStream(file_upload);
            fop.write(data_recevie);
            fop.close();
            System.out.println("upload file success !!!");
        }
        catch(Exception ex){
            System.out.println(ex);
        }
    }
    public void HandleSearch(ByteBuffer respone, InetAddress ip, int port)
    {
        int len = 0;
        String host_name = Util.Utils.GetHostName();
        ByteBuffer buf = ByteBuffer.allocate(Define.MAX_LEN);
        buf.putShort((short)host_name.getBytes().length);
        len += 2;
        buf.put(host_name.getBytes());
        len += host_name.getBytes().length;
        int num_file = 0;
        // tim file trong thu muc torrent
        try
        {
            Path directory = Paths.get("torrent/");
            try (DirectoryStream<Path> directoryStream = Files
            .newDirectoryStream(directory))
            {
                for(Path file : directoryStream) 
                {
                    byte[] file_name = file.getFileName().toString().getBytes();
                    buf.putShort((short)file_name.length);
                    len += 2;
                    buf.put(file_name);
                    len += file_name.length;
                    num_file ++;
                }
            }
        }
        catch(Exception ex)
        {
            
        }
        byte[] data = new byte[len + 4];
        
        ByteBuffer buf_temp = ByteBuffer.allocate(4);
        buf_temp.putInt(num_file);
        buf_temp.position(0);
        buf_temp.get(data,0,4);
        buf.position(0);
        buf.get(data,4,len);
        Send send = new Send();
       
        send.SendRespondSearch(data, ip, port);
    }
    public void HandleRespondSearch(ByteBuffer respone, InetAddress ip, int port)
    {
        int num_file = respone.getInt();
        int len_host_name = (int)respone.getShort();
        byte[] host_name = new byte[len_host_name];
        respone.get(host_name,0,len_host_name);
        for(int i = 0;i<num_file;i++)
        {
            int len = respone.getShort();
            byte[] file_name = new byte[len];
            respone.get(file_name, 0, len);
            String str_file_name = new String(file_name);
            // remove .torrent in file name
            str_file_name = Util.Utils.Remove(str_file_name,".torrent");
            main.AddFile(new String(host_name),str_file_name);
        }
        System.out.println("end");
    }
    public void HandleConfig(ByteBuffer respone, InetAddress ip, int port)
    {
        
    }
    public void HandleRespondConfig(ByteBuffer respone, InetAddress ip, int port)
    {
      
    }
}
